package com.iretailer.report.service.report;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.iretailer.report.base.BaseAction;
import com.iretailer.report.dto.*;
import com.iretailer.report.dto.report.ReGroupBy;
import com.iretailer.report.dto.report.ReReport;
import com.iretailer.report.model.*;
import com.iretailer.report.util.BeanUtils;
import com.iretailer.report.util.TimeUtil;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;

import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Auther: xjw
 * @Date: 2019/1/28 14:13
 * @Description:
 */
@Component
public class ReportAsync extends BaseAction {

    /**
     * @Author xjw
     * @Date 2019/2/14 11:11
     * @Description : 得到 案场 以及 区域
     **/
    public boolean getMallAndPhysicalZone(ReReport reReport, List<PhysicalZoneMall> physicalZones, List<DeviceMall> deviceMalls, List<Malls> malls) throws Exception {
        List<String> mallIds = reReport.getLocations().getMallIds();
        List<String> phyIds = reReport.getLocations().getPhyIds();
        List<String> deviceIds = reReport.getLocations().getDeviceIds();
        boolean result = false;
        if (mallIds != null && mallIds.size() > 0) {
            malls.addAll(mallsService.list(new QueryWrapper<Malls>().lambda().in(Malls::getId, mallIds).orderByAsc(Malls::getRanked).orderByAsc(Malls::getName)));
            physicalZones.addAll(physicalzonesService.selectByMallIds_ZoneTypeIds(mallIds, reReport.getLocations().getZoneType()));
            result = true;
        } else if (phyIds != null && phyIds.size() > 0) {
            physicalZones.addAll(physicalzonesService.selectByIds(phyIds));
            Set<String> mallIdsSet = new HashSet<>();
            for (PhysicalZoneMall pz : physicalZones) {
                mallIdsSet.add(pz.getMallId());
            }
            mallIds = new ArrayList<>(mallIdsSet);
            reReport.getLocations().setMallIds(mallIds);
            malls.addAll(mallsService.list(new QueryWrapper<Malls>().lambda().in(Malls::getId, reReport.getLocations().getMallIds())));
            result = true;
        }
        if (deviceIds != null && deviceIds.size() > 0) {
            deviceMalls.addAll(devicesService.selectDeviceMall(new QueryWrapper<DeviceMall>().lambda().in(DeviceMall::getId, deviceIds)));
        }
        // 设置所有案场中最大的时间
        if (mallIds.size() != 0) {
            BusinessTimes businessTimes = businessTimeService.selectMaxBusinessTimes(mallIds);
            if (reReport.getStartTime() == null) {
                reReport.setStartTime(businessTimes.getStartTime());
            }
            if (reReport.getEndTime() == null) {
                reReport.setEndTime(businessTimes.getEndTime());
            }
        } else {
            throw new Exception("mallId is empty");
        }
        return result;
    }


    public void setWeather(List<GlobalReportModel> globalReportModels, Map<String, Weathers> weatherMap) {
        if (weatherMap != null) {
            globalReportModels.forEach(it -> {
                if (it.getMallId() != null && it.getDateTime().length() > 10) {
                    String key = MessageFormat.format("{0}_{1}", it.getMallId(), it.getDateTime().substring(0, 10));
                    Weathers w = weatherMap.get(key);
                    if (w != null) {
                        it.setTempUnit(w.getTempUnit());
                        it.setPm25(w.getPm25());
                        it.setLowTemp(w.getLowTemp().intValue());
                        it.setHighTemp(w.getHighTemp().intValue());
                        it.setWeatherName(w.getWeatherName());
                        it.setDayPictureUrl(w.getDayPictureUrl());
                        it.setNightPictureUrl(w.getNightPictureUrl());
                    }
                }
            });
        }
    }

    public void setFlow(List<GlobalReportModel> globalReportModels, List<CameraFlowModel> cameraFlowModels, ReGroupBy groupMap) {
        if (cameraFlowModels != null && cameraFlowModels.size() > 0) {
            String timeFormatter = groupMap.getTimeFormatter();
            List<CameraFlowModel> cfms = new ArrayList<>();
            for (CameraFlowModel cfm : cameraFlowModels) {
                CameraFlowModel cf = new CameraFlowModel();
                BeanUtils.copyPropertiesASM(cfm, cf);
                switch (timeFormatter) {
                    case "All":
                        cf.setDateTime("All");
                        break;
                    case "Week":
                        cf.setDateTime(TimeUtil.getThisWeekMonday(TimeUtil.parse_LocalDateTime(cf.getDateTime()).toLocalDate()));
                        break;
                    case "Weekday":
                        cf.setDateTime(TimeUtil.getWeekOfLocalDate(TimeUtil.parse_LocalDateTime(cf.getDateTime()).toLocalDate()));
                        break;
                    case "WeekPeak":
                        LocalDateTime wdt = TimeUtil.parse_LocalDateTime(cf.getDateTime());
                        LocalDateTime w = LocalDateTime.of(TimeUtil.parse_LocalDate(TimeUtil.getThisWeekMonday(wdt.toLocalDate())), wdt.toLocalTime());
                        cf.setDateTime(TimeUtil.localDateTime_format(w, "yyyy-MM-dd HH:mm"));
                        break;
                    case "MonthPeak":
                        LocalDateTime mdt = TimeUtil.parse_LocalDateTime(cf.getDateTime());
                        LocalDateTime m = LocalDateTime.of(mdt.getYear(), mdt.getMonth(), 1, mdt.getHour(), mdt.getMinute());
                        cf.setDateTime(TimeUtil.localDateTime_format(m, "yyyy-MM-dd HH:mm"));
                        break;
                    case "YearPeak":
                        LocalDateTime ydt = TimeUtil.parse_LocalDateTime(cf.getDateTime());
                        LocalDateTime y = LocalDateTime.of(ydt.getYear(), 1, 1, ydt.getHour(), ydt.getMinute());
                        cf.setDateTime(TimeUtil.localDateTime_format(y, "yyyy-MM-dd HH:mm"));
                        break;
                    default:
                        LocalDateTime ldt = TimeUtil.parse_LocalDateTime(cf.getDateTime());
                        cf.setDateTime(TimeUtil.localDateTime_format(ldt, timeFormatter));
                        break;
                }
                cfms.add(cf);
            }
            Map<String, List<CameraFlowModel>> group_flow = null;
            switch (groupMap.getDomain()) {
                case "All":
                    group_flow = cfms.stream().collect(Collectors.groupingBy(CameraFlowModel::getAll_dateTime_Group));
                    break;
                case "Tag":
//                    Map<String, List<CameraFlowModel>> mall_flow = cfms.stream().collect(Collectors.groupingBy(CameraFlowModel::getMall_dateTime_Group));
                    Map<String, List<CameraFlowModel>> mall_flow = cfms.stream().collect(Collectors.groupingBy(CameraFlowModel::getMallId));
                    group_flow = new HashMap<>();
                    for (GlobalReportModel grm : globalReportModels) {
                        List<CameraFlowModel> flow = new ArrayList<>();
                        grm.getMallIds().forEach(mallId -> {
                            List<CameraFlowModel> find = mall_flow.get(mallId);
                            if (find != null) {
                                flow.addAll(find);
                            }
                        });
                        flow.forEach(it -> {
                            it.setTagTypeId(grm.getTagTypeId());
                        });
                        Map<String, List<CameraFlowModel>> tag_flow = flow.stream().collect(Collectors.groupingBy(CameraFlowModel::getTagType_dateTime_Group));
                        group_flow.putAll(tag_flow);
//                        group_flow.put(grm.getTagTypeId(), flow);
                    }
                    break;
                case "Mall":
                    group_flow = cfms.stream().collect(Collectors.groupingBy(CameraFlowModel::getMall_dateTime_Group));
                    break;
                case "Zone":
                    group_flow = cfms.stream().collect(Collectors.groupingBy(CameraFlowModel::getZone_dateTime_Group));
                    break;
                case "Week":
                    group_flow = cfms.stream().collect(Collectors.groupingBy(CameraFlowModel::getWeek_dateTime_Group));
                    break;
            }
            joinTime_model(globalReportModels, groupMap.getDomain(), group_flow);
        }
    }

    public void setPassByFlow(List<GlobalReportModel> globalReportModels, List<PassByModel> passByModels, ReGroupBy groupMap, boolean flowStatus) {
        if (passByModels != null && passByModels.size() > 0) {
            String timeFormatter = groupMap.getTimeFormatter();
            List<PassByModel> pbms = new ArrayList<>(passByModels.size());
            for (PassByModel pbm : passByModels) {
                PassByModel p = new PassByModel();
                BeanUtils.copyPropertiesASM(pbm, p);
                switch (timeFormatter) {
                    case "All":
                        p.setDateTime("All");
                        break;
                    case "Week":
                        p.setDateTime(TimeUtil.getThisWeekMonday(TimeUtil.parse_LocalDateTime(p.getDateTime()).toLocalDate()));
                        break;
                    case "Weekday":
                        p.setDateTime(TimeUtil.getWeekOfLocalDate(TimeUtil.parse_LocalDateTime(p.getDateTime()).toLocalDate()));
                        break;
                    default:
                        LocalDateTime ldt = TimeUtil.parse_LocalDateTime(p.getDateTime());
                        p.setDateTime(TimeUtil.localDateTime_format(ldt, timeFormatter));
                        break;
                }
                pbms.add(p);
            }
            Map<String, List<PassByModel>> group_passBy = null;
            switch (groupMap.getDomain()) {
                case "All":
                    group_passBy = pbms.stream().collect(Collectors.groupingBy(PassByModel::getAll_dateTime_Group));
                    break;
                case "Tag":
                    group_passBy = pbms.stream().collect(Collectors.groupingBy(PassByModel::getTagType_dateTime_Group));
                    break;
                case "Mall":
                    group_passBy = pbms.stream().collect(Collectors.groupingBy(PassByModel::getMall_dateTime_Group));
                    break;
                case "Zone":
                    group_passBy = pbms.stream().collect(Collectors.groupingBy(PassByModel::getZone_dateTime_Group));
                    break;
                case "Week":
                    group_passBy = pbms.stream().collect(Collectors.groupingBy(PassByModel::getWeek_dateTime_Group));
                    break;
            }
            joinTime_model_passBy(globalReportModels, groupMap.getDomain(), group_passBy, flowStatus);
        }
    }

    public void joinTime_model_alarmTime(List<GlobalReportModel> globalReportModels, String domain, Map<String, List<EasAlarmModel>> group_alarmTime) {
        for (GlobalReportModel grm : globalReportModels) {
            List<EasAlarmModel> easAlarmModels = group_alarmTime.get(common_key(grm, domain));
            if (easAlarmModels != null) {
                for (EasAlarmModel e : easAlarmModels) {
                    grm.setAlarmTimes(grm.getAlarmTimes() + e.getAlarmTimes());
                }
            }
        }
    }

    public void joinTime_model_passBy(List<GlobalReportModel> globalReportModels, String domain, Map<String, List<PassByModel>> group_passBy, boolean flowStatus) {
        for (GlobalReportModel grm : globalReportModels) {
            passFlatMap(grm, group_passBy, common_key(grm, domain), flowStatus);
        }
    }

    public void joinTime_model(List<GlobalReportModel> globalReportModels, String domain, Map<String, List<CameraFlowModel>> group_flow) {
        for (GlobalReportModel grm : globalReportModels) {
            String key = common_key(grm, domain);
            flowFlatMap(grm, group_flow, key);
        }
    }

    private void joinTime_model_dwellTime(List<GlobalReportModel> globalReportModels, String domain, Map<String, List<DwellTimeModel>> group_dwellTime) {
        for (GlobalReportModel grm : globalReportModels) {
            dwellFlatMap(grm, group_dwellTime, common_key(grm, domain));
        }
    }


    public void dwellFlatMap(GlobalReportModel grm, Map<String, List<DwellTimeModel>> group_dwellTime, String key) {
        List<DwellTimeModel> dtms = group_dwellTime.get(key);
        if (dtms != null) {
            int entranceCount = 0;
            int passCount = 0;
            int stayCount = 0;
            int secondsCount = 0;
            for (DwellTimeModel d : dtms) {
                entranceCount += d.getEntranceCount();
                passCount += d.getPassCount();
                stayCount += d.getStayCount();
                secondsCount += d.getSecondsCount();
            }
            grm.setEntranceCount(entranceCount);
            grm.setPassCount(passCount);
            grm.setStayCount(stayCount);
            grm.setSecondsCount(secondsCount);
        }
    }

    public void passFlatMap(GlobalReportModel grm, Map<String, List<PassByModel>> group_passBy, String key, boolean flowStatus) {
        List<PassByModel> passBy = group_passBy.get(key);
        if (passBy != null) {
            for (PassByModel p : passBy) {
                if (!flowStatus) {
                    grm.setEnter(grm.getEnter() + p.getCountEnter());
                }
                grm.setPassBy(grm.getPassBy() + p.getCountPass());
            }
        }
    }

    public void flowFlatMap(GlobalReportModel grm, Map<String, List<CameraFlowModel>> group_flow, String key) {
        List<CameraFlowModel> flow = group_flow.get(key);
        if (flow != null) {
            for (CameraFlowModel f : flow) {
                grm.setEnter(grm.getEnter() + f.getCountenter());
                grm.setExit(grm.getExit() + f.getCountexit());
            }
        }
    }


    /**
     * @Author xjw
     * @Date 2019/2/14 14:20
     * @Description :  得到 startDate 和 endDate 之间的 所有 Malls 的 天气
     **/
    public Map<String, Weathers> weather(List<Malls> malls, String startDate, String endDate) {
        Map<String, List<Malls>> mallsMap = malls.stream().collect(Collectors.groupingBy(Malls::getCityId));
        if (mallsMap != null && mallsMap.keySet().size() > 0) {
            List<Weathers> weathers = weatherService
                    .list(new QueryWrapper<Weathers>().lambda()
                            .between(Weathers::getDate, startDate, endDate)
                            .in(Weathers::getCityId, mallsMap.keySet()));
//            return weathers.stream().collect(Collectors.toMap(Weathers::getCityId_date, a -> a, (key1, key2) -> key1));
            Map<String, Weathers> result = new HashMap<>();
            weathers.forEach(w -> {
                malls.forEach(m -> {
                    if (m.getCityId().equals(w.getCityId())) {
                        result.put(MessageFormat.format("{0}_{1}", m.getId(), TimeUtil.localDate_format(w.getDate())), w);
                    }
                });
            });
            return result;
        } else {
            return new HashMap<>();
        }

    }


    public void setDwellTimeModel(List<GlobalReportModel> globalReportModels, List<DwellTimeModel> dwellTimeModels, ReGroupBy groupMap) {
        if (dwellTimeModels != null && dwellTimeModels.size() > 0) {
            String timeFormatter = groupMap.getTimeFormatter();
            List<DwellTimeModel> dtms = new ArrayList<>(dwellTimeModels.size());
            for (DwellTimeModel d : dwellTimeModels) {
                DwellTimeModel dtm = new DwellTimeModel();
                BeanUtils.copyPropertiesASM(d, dtm);
                switch (timeFormatter) {
                    case "All":
                        dtm.setDateTime("All");
                        break;
                    case "Week":
                        dtm.setDateTime(TimeUtil.getThisWeekMonday(TimeUtil.parse_LocalDateTime(dtm.getDateTime()).toLocalDate()));
                        break;
                    case "Weekday":
                        dtm.setDateTime(TimeUtil.getWeekOfLocalDate(TimeUtil.parse_LocalDateTime(dtm.getDateTime()).toLocalDate()));
                        break;
                    default:
                        LocalDateTime ldt = TimeUtil.parse_LocalDateTime(dtm.getDateTime());
                        dtm.setDateTime(TimeUtil.localDateTime_format(ldt, timeFormatter));
                        break;
                }
                dtms.add(dtm);
            }
            Map<String, List<DwellTimeModel>> group_dwellTime = null;
            switch (groupMap.getDomain()) {
                case "All":
                    group_dwellTime = dtms.stream().collect(Collectors.groupingBy(DwellTimeModel::getAll_dateTime_Group));
                    break;
                case "Mall":
                    group_dwellTime = dtms.stream().collect(Collectors.groupingBy(DwellTimeModel::getMall_dateTime_Group));
                    break;
                case "Zone":
                    group_dwellTime = dtms.stream().collect(Collectors.groupingBy(DwellTimeModel::getZone_dateTime_Group));
                    break;
                case "Week":
                    group_dwellTime = dtms.stream().collect(Collectors.groupingBy(DwellTimeModel::getWeek_dateTime_Group));
                    break;
            }
            joinTime_model_dwellTime(globalReportModels, groupMap.getDomain(), group_dwellTime);
        }
    }


    private String common_key(GlobalReportModel grm, String domain) {
        String key = "";
        switch (domain) {
            case "All":
                key = MessageFormat.format("{0}_{1}", grm.getDomainLabel(), grm.getTimeLabel());
                break;
            case "Tag":
                key = String.format("%s_%s", grm.getTagTypeId(), grm.getTimeLabel());
                break;
            case "Mall":
                key = MessageFormat.format("{0}_{1}", grm.getMallId(), grm.getTimeLabel());
                break;
            case "Zone":
                key = MessageFormat.format("{0}_{1}", grm.getPhyId(), grm.getTimeLabel());
                break;
            case "Device":
                key = MessageFormat.format("{0}_{1}", grm.getDeviceId(), grm.getTimeLabel());
                break;
            case "Week":
                key = MessageFormat.format("{0}_{1}", grm.getDomainLabel(), grm.getTimeLabel());
                break;
        }
        return key;
    }

    public void setFace(List<GlobalReportModel> globalReportModels, List<GenderAgeImage> genders, List<GenderAgeImage> ageTypes, Map<String, Categories> ageTypeMap) {
        if (genders != null) {
            // 1为男性 0 为女性
            Map<Integer, GenderAgeImage> map = genders.stream().collect(Collectors.toMap(GenderAge::getGender, a -> a));
            int males = 0;
            int females = 0;
            for (Map.Entry<Integer, GenderAgeImage> m : map.entrySet()) {
                switch (m.getKey()) {
                    case 1:
                        males = m.getValue().getNumber();
                        break;
                    case 0:
                        females = m.getValue().getNumber();
                        break;
                }
                if (m.getKey() == 1) {

                }
            }
            for (GlobalReportModel g : globalReportModels) {
                g.setMale(males);
                g.setFemale(females);
            }
        }
        if (ageTypes != null) {
            // 年龄区间，1: [0-18], 2: [19-35], 3: [36-55], 4 56+
            Map<String, GenderAgeImage> map = ageTypes.stream().collect(Collectors.toMap(GenderAge::getAgeType, a -> a));
            Map<String, Integer> ageRatio = new HashMap<>(4);
            List<String> agetTypes = new ArrayList<>();
            ageTypeMap.entrySet().forEach(it -> {
                agetTypes.add(it.getValue().getData());
                ageRatio.put(it.getValue().getData(), 0);
            });
//            ageRatio.put("0-18", 0);
//            ageRatio.put("19-35", 0);
//            ageRatio.put("36-55", 0);
//            ageRatio.put("56+", 0);
            map.entrySet().forEach(it -> {
                String key = it.getKey();
                if (ageRatio.get(key) != null) {
                    ageRatio.put(key, it.getValue().getNumber());
                }
            });
            for (GlobalReportModel g : globalReportModels) {
                g.setAgeTypes(agetTypes);
                g.setAgeRatio(ageRatio);
            }
        }
    }

    public void setSales(List<GlobalReportModel> globalReportModels, List<Sales> salesList, ReGroupBy groupMap) {
        if (CollectionUtils.isEmpty(salesList)) {
            return;
        }
        String timeFormatter = groupMap.getTimeFormatter();
        Map<String, Sales> salesMap = new HashMap<>();
        switch (timeFormatter) {
            case "All":
                salesMap = salesList.stream().collect(Collectors.toMap(Sales::getMallId, a -> a, (o, n) -> {
                    o.setSales(o.getSales() + n.getSales());
                    o.setReceipts(o.getReceipts()+n.getReceipts());
                    o.setPieces(o.getPieces()+n.getPieces());
                    return o;
                }));
                break;
            case "yyyy-MM-dd":
                salesMap = salesList.stream().collect(Collectors.toMap(Sales::getMallId_Date, a -> a, (o, n) -> {
                    o.setSales(o.getSales() + n.getSales());
                    o.setReceipts(o.getReceipts()+n.getReceipts());
                    o.setPieces(o.getPieces()+n.getPieces());
                    return o;
                }));
                break;
        }
        for (GlobalReportModel g : globalReportModels) {
            String key = null;
            switch (timeFormatter) {
                case "All":
                    key = g.getMallId();
                    break;
                case "yyyy-MM-dd":
                    key = g.getMallId() + "_" + g.getDate();
                    break;
            }
            if (salesMap.containsKey(key)) {
                Sales sales = salesMap.get(key);
                g.setSales(sales.getSales());
                g.setReceipts(sales.getReceipts());
                g.setPieces(sales.getPieces());
            }
        }

    }

    public void setEasAlarm(List<GlobalReportModel> globalReportModels, List<EasAlarmModel> easAlarmModels, ReGroupBy groupMap) {
        if (easAlarmModels != null && easAlarmModels.size() > 0) {
            String timeFormatter = groupMap.getTimeFormatter();
            List<EasAlarmModel> eams = new ArrayList<>();
            for (EasAlarmModel eam : easAlarmModels) {
                EasAlarmModel e = new EasAlarmModel();
                BeanUtils.copyPropertiesASM(eam, e);
                switch (timeFormatter) {
                    case "All":
                        e.setDateTime("All");
                        break;
                    case "Week":
                        e.setDateTime(TimeUtil.getThisWeekMonday(TimeUtil.parse_LocalDateTime(e.getDateTime()).toLocalDate()));
                        break;
                    case "Weekday":
                        e.setDateTime(TimeUtil.getWeekOfLocalDate(TimeUtil.parse_LocalDateTime(e.getDateTime()).toLocalDate()));
                        break;
                    case "WeekPeak":
                        LocalDateTime wdt = TimeUtil.parse_LocalDateTime(e.getDateTime());
                        LocalDateTime w = LocalDateTime.of(TimeUtil.parse_LocalDate(TimeUtil.getThisWeekMonday(wdt.toLocalDate())), wdt.toLocalTime());
                        e.setDateTime(TimeUtil.localDateTime_format(w, "yyyy-MM-dd HH:mm"));
                        break;
                    case "MonthPeak":
                        LocalDateTime mdt = TimeUtil.parse_LocalDateTime(e.getDateTime());
                        LocalDateTime m = LocalDateTime.of(mdt.getYear(), mdt.getMonth(), 1, mdt.getHour(), mdt.getMinute());
                        e.setDateTime(TimeUtil.localDateTime_format(m, "yyyy-MM-dd HH:mm"));
                        break;
                    case "YearPeak":
                        LocalDateTime ydt = TimeUtil.parse_LocalDateTime(e.getDateTime());
                        LocalDateTime y = LocalDateTime.of(ydt.getYear(), 1, 1, ydt.getHour(), ydt.getMinute());
                        e.setDateTime(TimeUtil.localDateTime_format(y, "yyyy-MM-dd HH:mm"));
                        break;
                    default:
                        LocalDateTime ldt = TimeUtil.parse_LocalDateTime(e.getDateTime());
                        e.setDateTime(TimeUtil.localDateTime_format(ldt, timeFormatter));
                        break;
                }
                eams.add(e);
            }
            Map<String, List<EasAlarmModel>> group_alarmTime = null;
            switch (groupMap.getDomain()) {
                case "All":
                    group_alarmTime = eams.stream().collect(Collectors.groupingBy(EasAlarmModel::getAll_dateTime_Group));
                    break;
                case "Mall":
                    group_alarmTime = eams.stream().collect(Collectors.groupingBy(EasAlarmModel::getMall_dateTime_Group));
                    break;
                case "Zone":
                case "Device":
                    group_alarmTime = eams.stream().collect(Collectors.groupingBy(EasAlarmModel::getDevice_dateTime_Group));
                    break;
                case "Week":
                    group_alarmTime = eams.stream().collect(Collectors.groupingBy(EasAlarmModel::getWeek_dateTime_Group));
                    break;
            }
            joinTime_model_alarmTime(globalReportModels, groupMap.getDomain(), group_alarmTime);
        }
    }
}
